#include "rtda/heap/method.h"
#include "classfile/class_file.hpp"
#include "rtda/heap/class.h"

//#include "rtda/heap/method_descriptor_parser.h"

namespace jvm {
namespace heap {

Method::Method(std::shared_ptr<Class> pClass, const std::shared_ptr<classfile::MemberInfo> pMemberInfo)
{
    m_pClass = pClass;
    copyMemberInfo(pMemberInfo);
    copyAttributes(pMemberInfo);
    auto parsedDescriptor = parseMethodDescriptor(m_descriptor);
    calcArgSlotCount(parsedDescriptor);
    if (isNative())
    {
        injectCodeAttribute(parsedDescriptor->returnType);
    }
}

std::vector<std::shared_ptr<Method>> Method::newMethods(std::shared_ptr<Class> pClass, const std::vector<std::shared_ptr<classfile::MemberInfo>>& cfMethods) {
    std::vector<std::shared_ptr<Method>> methods;
    methods.reserve(cfMethods.size());
    
    for (const auto& pMemberInfo : cfMethods) {
        methods.push_back(std::make_shared<Method>(pClass, pMemberInfo));
    }
    
    return methods;
}

void Method::injectCodeAttribute(const std::string& returnType)
{
    m_maxStack = 4; // todo
    m_maxLocals = m_argSlotCount;

    if (returnType.empty()) {
        m_code = { 0xfe, 0xac }; // default to ireturn
        return;
    }

    switch (returnType[0]) {
    case 'V':
        m_code = { 0xfe, 0xb1 }; // return
        break;
    case 'L':
    case '[':
        m_code = { 0xfe, 0xb0 }; // areturn
        break;
    case 'D':
        m_code = { 0xfe, 0xaf }; // dreturn
        break;
    case 'F':
        m_code = { 0xfe, 0xae }; // freturn
        break;
    case 'J':
        m_code = { 0xfe, 0xad }; // lreturn
        break;
    default:
        m_code = { 0xfe, 0xac }; // ireturn
    }
}

void Method::copyAttributes(const std::shared_ptr<classfile::MemberInfo> pMemberInfo) {
    auto pCodeAttr = pMemberInfo->getCodeAttribute();
    if (nullptr != pCodeAttr) {
        m_maxStack = pCodeAttr->getMaxStack();
        m_maxLocals = pCodeAttr->getMaxLocals();
        m_code = pCodeAttr->getCode();
        m_pLineNumberTable = pCodeAttr->getLineNumberTableAttribute();
        m_pExceptionTable = std::make_shared<ExceptionTable>(pCodeAttr->getExceptionTable(), m_pClass->getConstantPool());
    }
}

void Method::calcArgSlotCount(const std::unique_ptr<MethodDescriptor>& parsedDescriptor) {
    for (const auto& paramType : parsedDescriptor->parameterTypes) {
        m_argSlotCount++;
        if (paramType == "J" || paramType == "D") {
            m_argSlotCount++;  // Long and double take two slots
        }
    }

    if (!isStatic()) {
        m_argSlotCount++;  // Add slot for 'this' reference in non-static methods
    }
}

bool Method::isSynchronized() const { return 0 != (m_accessFlags & AccessFlags::ACC_SYNCHRONIZED); }
bool Method::isBridge() const { return 0 != (m_accessFlags & AccessFlags::ACC_BRIDGE); }
bool Method::isVarargs() const { return 0 != (m_accessFlags & AccessFlags::ACC_VARARGS); }
bool Method::isNative() const { return 0 != (m_accessFlags & AccessFlags::ACC_NATIVE); }
bool Method::isAbstract() const { return 0 != (m_accessFlags & AccessFlags::ACC_ABSTRACT); }
bool Method::isStrict() const { return 0 != (m_accessFlags & AccessFlags::ACC_STRICT); }

uint32_t Method::getMaxStack() const { return m_maxStack; }
uint32_t Method::getMaxLocals() const { return m_maxLocals; }
const std::vector<uint8_t>& Method::getCode() const { return m_code; }
uint32_t Method::getArgSlotCount() const { return m_argSlotCount; }

int Method::getLineNumber(const int& pc) const
{
    if (isNative()) return -2;
    if (!m_pLineNumberTable) return -1;

    return m_pLineNumberTable->getLineNumber(pc);
}


int Method::findExceptionHandler(std::shared_ptr<Class> pExClass, const int& pc) const
{
    auto phandler = m_pExceptionTable->findExceptionHandler(pExClass, pc);
    if (phandler)
    {
        return phandler->handlerPc;
    }
    return -1;
}


} // namespace heap
} // namespace jvm